﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using Microsoft.Phone.Tasks;
using Microsoft.Xna.Framework.Media.PhoneExtensions;
using SAFPodcast.WP8.Common.Tools;
using SAFPodcast.WP8.DAL.FileSystem;
using SAFPodcast.WP8.DAL.LocalBDD;
using SAFPodcast.WP8.DAL.Web;
using SAFPodcast.WP8.DTO.Entities;
using SAFPodcast.WP8.DTO.TO;
using SAFPodcast.WP8.Fwk.Helper;

namespace SAFPodcast.WP8.Model.Services
{
    public class DataService : IDataService
    {
        private IWebAO webAO;

        public DataService(IWebAO webAO)
        {
            this.webAO = webAO;
        }

        #region Technical
        public void InitializeDB()
        {
            DataContextHelper.InitializeDB();
        }
        #endregion

        private PodcastsDataContext GetDataContext()
        {
            return new PodcastsDataContext(PodcastsDataContext.DBConnectionString);
        }

        private const string genericName = "SAFPodcast";
        public void DownloadPodcast(Uri uri, Action<DownloadDataItem, Exception> callback)
        {
            SongMetadata song = new SongMetadata()
            {
                AlbumArtistName = genericName,
                AlbumName = genericName,
                ArtistName = genericName,
                GenreName = "Podcast",
                Name = GetNameByUri(uri),
                AlbumReleaseDate = DateTime.Today
            };
            webAO.DownloadSingleFile(
                song,
                uri,
                callback);
        }

        private string GetNameByUri(Uri uri)
        {
            var result = uri.NullsafeToString();
            try
            {
                var questionMarkIndex = result.IndexOf('?');
                if (questionMarkIndex != -1)
                {
                    result = result.Substring(0, questionMarkIndex);
                }

                var lastSlashIndex = result.LastIndexOf('/');
                result = result.Substring(lastSlashIndex + 1);

                result = DateTime.Now.ToString("yyyy-MM-dd_HH:mm") + "_" + result;

                return result;
            }
            catch (Exception ex)
            {
                LogHelper.Error(
                    String.Format("Erreur lors de la génération du nom du fichier : \"{0}\"", result),
                    ex);
                return DateTime.Now.ToString("yyyy-MM-dd_HH:mm") + "_" + genericName;
            }
        }

        public Feed AddFeed(String uriAsString, String name)
        {
            var feed = new Feed()
            {
                URI = uriAsString,
                LastUpdate = DateTime.Now.AddDays(-30),
                Name = name,
            };

            using (var dataContext = GetDataContext())
            {
                dataContext.Feeds.InsertOnSubmit(feed);
                dataContext.SubmitChanges();
            }

            return feed;
        }

        public void DeleteFeed(Feed feed)
        {

            using (var dataContext = GetDataContext())
            {
                var item = dataContext.Feeds.FirstOrDefault(x => x.Id == feed.Id);
                if (item != null)
                {
                    dataContext.Feeds.DeleteOnSubmit(item);
                    dataContext.SubmitChanges();
                }
            }
        }

        public void TestFeed(Uri uri, Action<bool> callback)
        {
            webAO.Validade(uri, callback);
        }

        public Feed UpdateFeed(Feed selectedFeed, string urlAsString, string name)
        {
            using (var dataContext = GetDataContext())
            {
                var result = new Feed();

                var item = dataContext.Feeds.FirstOrDefault(x => x.Id == selectedFeed.Id);
                if (item != null)
                {
                    result.LastUpdate = item.LastUpdate;
                    dataContext.Feeds.DeleteOnSubmit(item);
                }

                result.URI = urlAsString;
                result.Name = name;
                dataContext.Feeds.InsertOnSubmit(result);

                return result;
            }
        }

        public void LoadAndParseFeed(Feed feed, Action<List<FeedItem>, Exception> onSuccess)
        {
            Uri uri = null;
            if (Uri.TryCreate(feed.URI, UriKind.Absolute, out uri))
            {
                webAO.DownloadFeedText(uri,
                    (txt, ex) =>
                    {
                        if (ex == null)
                        {
                            this.ParseAsync(txt, (list) => { list.ForEach(x => x.Feed = feed); onSuccess(list, null); });
                        }
                        else
                        {
                            onSuccess(null, ex);
                        }
                    });
            }
        }

        public void GetFeedName(Uri uri, Action<string, Exception> callback)
        {
            webAO.DownloadFeedText(uri,
                (txt, ex) =>
                {
                    if (ex == null)
                    {
                        try
                        {
                            String name = XElement.Parse(txt)
                                .Element(Consts.Xml.CHANNEL)
                                .Element(Consts.Xml.TITLE)
                                .Value;
                            callback(name, null);
                        }
                        catch (Exception e)
                        {
                            LogHelper.Error("Le parse du nom du flux a échoué.", e);
                            callback(null, e);
                        }
                    }
                    else
                    {
                        callback(null, ex);
                    }
                });
        }

        public void ParseAsync(String xml, Action<List<FeedItem>> callback)
        {
            new Thread(() =>
            {
                List<FeedItem> feedItems = new List<FeedItem>();
                var rss = XElement.Parse(xml);

                var channel = rss.Element(Consts.Xml.CHANNEL);

                var title = channel.Element(Consts.Xml.TITLE).Value;

                // Download image
                var imageUrl = channel.Element(Consts.Xml.IMAGE).Element(Consts.Xml.URL).Value;

                var items = channel.Elements(Consts.Xml.ITEM);

                DateTime pubDate = default(DateTime);
                Uri itemUri = default(Uri);

                foreach (var item in items)
                {
                    var itemTitle = item.Element(Consts.Xml.TITLE).Value;
                    var pubDateAsText = item.Element(Consts.Xml.PUBDATE).Value;
                    var itemUriAsText = item.Element(Consts.Xml.ENCLOSURE).Attribute(Consts.Xml.URL).Value;

                    if (DateTime.TryParse(pubDateAsText, CultureInfo.InvariantCulture.DateTimeFormat, DateTimeStyles.None, out pubDate))
                    {
                        if (Uri.TryCreate(itemUriAsText, UriKind.Absolute, out itemUri))
                        {
                            feedItems.Add(new FeedItem()
                            {
                                Uri = itemUri,
                                Title = itemTitle,
                                PublicationDate = pubDate,
                            });
                        }
                        else
                        {
                            LogHelper.Debug("Invalid URI : " + itemUriAsText);
                        }
                    }
                    else
                    {
                        LogHelper.Debug("Invalid date : " + pubDateAsText);
                    }
                }
                callback(feedItems);
            }).Start();
        }

        public void DownloadFeedItem(FeedItem feedItem, Action<DownloadFeedItemDataItem, Exception> callback)
        {
            var song = new SongMetadata()
            {
                AlbumName = feedItem.Feed.Name,
                ArtistName = feedItem.Feed.Name,
                AlbumArtistName = feedItem.Feed.Name,
                GenreName = "Podcast",
                Name = feedItem.Title,
                AlbumReleaseDate = DateTime.Today
            };

            webAO.DownloadSingleFile(song, feedItem.Uri,
                (ddi, e) =>
                {
                    ddi = ddi ?? new DownloadDataItem(0, null, false, song);
                    callback(new DownloadFeedItemDataItem(feedItem, ddi), e);
                });
        }

        public void GetFeeds(Action<FeedListDataItem, Exception> callback)
        {
            using (var dataContext = GetDataContext())
            {
                var items = dataContext.Feeds.OrderBy(x => x.LastUpdate);
                callback(new FeedListDataItem(items), null);
            }
        }

        public void InitializeErrorManagement()
        {
            LogHelper.ExceptionRaised += (source, e) =>
            {
                using (var dataContext = GetDataContext())
                {
                    dataContext.ApplicationErrors.InsertOnSubmit(
                        new Errors()
                        {
                            Message = e.Message,
                            ExceptionStack = e.NullsafeToString()
                        }
                    );
                    dataContext.SubmitChanges();
                }
            };

            LogHelper.ExceptionRaised += (source, e) =>
            {
                System.Windows.MessageBox.Show(e.NullsafeToString());
            };
        }

        public void SendErrors()
        {
            using (var dataContext = GetDataContext())
            {
                EmailComposeTask emailComposeTask = new EmailComposeTask();

                emailComposeTask.Subject = "Errors List";
                emailComposeTask.Body = 
                    String.Join("\n",
                    dataContext.ApplicationErrors.Select((x) => x.ToString()));

                emailComposeTask.To = "julian.adler@outlook.com";

                emailComposeTask.Show();
            }
        }

        public void EmptyReceptionDir()
        {
            FileSystemAccessObject.EmptyReceptionDir();
        }
    }
}
